package com.gzc.just.play.last.war.sceneserver.integration;

import com.gzc.just.play.last.war.sceneserver.entity.Player;
import com.gzc.just.play.last.war.sceneserver.repository.PlayerRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestEntityManager;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 玩家Repository集成测试
 * 
 * 测试PlayerRepository的各种数据库操作
 * 作为Phase 4.3数据库持久化集成的一部分
 */
@SpringBootTest
@AutoConfigureTestDatabase
@AutoConfigureTestEntityManager
@ActiveProfiles("test")
@Transactional
@DisplayName("玩家Repository集成测试")
public class PlayerRepositoryIntegrationTest {
    
    @Autowired
    private PlayerRepository playerRepository;
    
    @Autowired
    private TestEntityManager entityManager;
    
    private Player testPlayer;
    
    @BeforeEach
    void setUp() {
        // 创建测试玩家
        testPlayer = new Player("testuser", "hashedPassword", "salt123", "Test Player");
        testPlayer.setLevel(5);
        testPlayer.setExperience(500L);
        testPlayer.setVipLevel(1);
        testPlayer.setIsOnline(true);
        testPlayer.setCreateTime(LocalDateTime.now().minusDays(1));
        testPlayer.setUpdateTime(LocalDateTime.now());
        testPlayer = playerRepository.save(testPlayer);
    }
    
    @Test
    @DisplayName("根据ID查找玩家")
    void testFindById() {
        Optional<Player> foundPlayer = playerRepository.findById(testPlayer.getPlayerId());
        
        assertTrue(foundPlayer.isPresent(), "应该找到玩家");
        assertEquals(testPlayer.getUsername(), foundPlayer.get().getUsername(), "用户名应该匹配");
        assertEquals(testPlayer.getNickname(), foundPlayer.get().getNickname(), "昵称应该匹配");
    }
    
    @Test
    @DisplayName("根据用户名查找玩家")
    void testFindByUsername() {
        Optional<Player> foundPlayer = playerRepository.findByUsername("testuser");
        
        assertTrue(foundPlayer.isPresent(), "应该找到玩家");
        assertEquals(testPlayer.getPlayerId(), foundPlayer.get().getPlayerId(), "玩家ID应该匹配");
    }
    
    @Test
    @DisplayName("根据昵称查找玩家")
    void testFindByNickname() {
        Optional<Player> foundPlayer = playerRepository.findByNickname("Test Player");
        
        assertTrue(foundPlayer.isPresent(), "应该找到玩家");
        assertEquals(testPlayer.getPlayerId(), foundPlayer.get().getPlayerId(), "玩家ID应该匹配");
    }
    
    @Test
    @DisplayName("根据用户名和密码哈希查找玩家")
    void testFindByUsernameAndPasswordHash() {
        Optional<Player> foundPlayer = playerRepository.findByUsernameAndPasswordHash("testuser", "hashedPassword");
        
        assertTrue(foundPlayer.isPresent(), "应该找到玩家");
        assertEquals(testPlayer.getPlayerId(), foundPlayer.get().getPlayerId(), "玩家ID应该匹配");
    }
    
    @Test
    @DisplayName("查找在线玩家")
    void testFindByIsOnlineTrue() {
        // 创建另一个离线玩家
        Player offlinePlayer = new Player("offlineuser", "hashedPassword", "salt456", "Offline Player");
        offlinePlayer.setIsOnline(false);
        playerRepository.save(offlinePlayer);
        
        List<Player> onlinePlayers = playerRepository.findByIsOnlineTrue();
        
        assertEquals(1, onlinePlayers.size(), "应该只有1个在线玩家");
        assertEquals(testPlayer.getPlayerId(), onlinePlayers.get(0).getPlayerId(), "应该找到测试玩家");
    }
    
    @Test
    @DisplayName("查找被封禁的玩家")
    void testFindByIsBannedTrue() {
        // 创建被封禁玩家
        Player bannedPlayer = new Player("banneduser", "hashedPassword", "salt789", "Banned Player");
        bannedPlayer.setIsBanned(true);
        bannedPlayer.setBanEndTime(LocalDateTime.now().plusDays(1));
        playerRepository.save(bannedPlayer);
        
        List<Player> bannedPlayers = playerRepository.findByIsBannedTrue();
        
        assertEquals(1, bannedPlayers.size(), "应该只有1个被封禁的玩家");
        assertEquals(bannedPlayer.getPlayerId(), bannedPlayers.get(0).getPlayerId(), "应该找到被封禁玩家");
    }
    
    @Test
    @DisplayName("根据等级范围查找玩家")
    void testFindByLevelBetween() {
        // 创建不同等级的玩家
        Player level3Player = new Player("level3user", "hashedPassword", "salt3", "Level 3 Player");
        level3Player.setLevel(3);
        playerRepository.save(level3Player);
        
        Player level10Player = new Player("level10user", "hashedPassword", "salt10", "Level 10 Player");
        level10Player.setLevel(10);
        playerRepository.save(level10Player);
        
        Pageable pageable = PageRequest.of(0, 10);
        Page<Player> players = playerRepository.findByLevelBetween(3, 10, pageable);
        
        assertEquals(3, players.getTotalElements(), "应该找到3个玩家");
    }
    
    @Test
    @DisplayName("统计在线玩家数量")
    void testCountByIsOnlineTrue() {
        // 创建另一个离线玩家
        Player offlinePlayer = new Player("offlineuser2", "hashedPassword", "salt4562", "Offline Player 2");
        offlinePlayer.setIsOnline(false);
        playerRepository.save(offlinePlayer);
        
        long onlineCount = playerRepository.countByIsOnlineTrue();
        
        assertEquals(1, onlineCount, "应该只有1个在线玩家");
    }
    
    @Test
    @DisplayName("统计指定等级的玩家数量")
    void testCountByLevel() {
        // 创建相同等级的玩家
        Player sameLevelPlayer = new Player("sameleveluser", "hashedPassword", "samesalt", "Same Level Player");
        sameLevelPlayer.setLevel(5);
        playerRepository.save(sameLevelPlayer);
        
        long level5Count = playerRepository.countByLevel(5);
        
        assertEquals(2, level5Count, "应该有2个5级玩家");
    }
    
    @Test
    @DisplayName("查找等级排行榜")
    void testFindLevelLeaderboard() {
        // 创建不同等级的玩家
        Player level10Player = new Player("level10user", "hashedPassword", "salt10", "Level 10 Player");
        level10Player.setLevel(10);
        level10Player.setExperience(2000L);
        playerRepository.save(level10Player);
        
        Player level8Player = new Player("level8user", "hashedPassword", "salt8", "Level 8 Player");
        level8Player.setLevel(8);
        level8Player.setExperience(1500L);
        playerRepository.save(level8Player);
        
        Pageable pageable = PageRequest.of(0, 10);
        Page<Player> leaderboard = playerRepository.findLevelLeaderboard(1, pageable);
        
        assertEquals(3, leaderboard.getTotalElements(), "应该有3个玩家");
        assertEquals(10, leaderboard.getContent().get(0).getLevel(), "最高等级应该是10");
        assertEquals(8, leaderboard.getContent().get(1).getLevel(), "第二高等级应该是8");
        assertEquals(5, leaderboard.getContent().get(2).getLevel(), "第三高等级应该是5");
    }
    
    @Test
    @DisplayName("更新玩家在线状态")
    void testUpdatePlayerOnlineStatus() {
        int updated = playerRepository.updatePlayerOnlineStatus(testPlayer.getPlayerId(), false, LocalDateTime.now());
        
        assertEquals(1, updated, "应该更新1个玩家");
        
        Optional<Player> updatedPlayer = playerRepository.findById(testPlayer.getPlayerId());
        assertTrue(updatedPlayer.isPresent(), "应该找到玩家");
        assertFalse(updatedPlayer.get().getIsOnline(), "玩家应该离线");
    }
    
    @Test
    @DisplayName("更新玩家等级和经验")
    void testUpdatePlayerLevelAndExperience() {
        int updated = playerRepository.updatePlayerLevelAndExperience(testPlayer.getPlayerId(), 6, 1000L);
        
        assertEquals(1, updated, "应该更新1个玩家");
        
        Optional<Player> updatedPlayer = playerRepository.findById(testPlayer.getPlayerId());
        assertTrue(updatedPlayer.isPresent(), "应该找到玩家");
        assertEquals(6, updatedPlayer.get().getLevel(), "等级应该更新为6");
        assertEquals(1000L, updatedPlayer.get().getExperience(), "经验应该更新为1000");
    }
    
    @Test
    @DisplayName("增加玩家经验")
    void testAddPlayerExperience() {
        int updated = playerRepository.addPlayerExperience(testPlayer.getPlayerId(), 500L, 6);
        
        assertEquals(1, updated, "应该更新1个玩家");
        
        Optional<Player> updatedPlayer = playerRepository.findById(testPlayer.getPlayerId());
        assertTrue(updatedPlayer.isPresent(), "应该找到玩家");
        assertEquals(1000L, updatedPlayer.get().getExperience(), "经验应该是500+500=1000");
        assertEquals(6, updatedPlayer.get().getLevel(), "等级应该更新为6");
    }
    
    @Test
    @DisplayName("解除玩家封禁")
    void testUnbanPlayer() {
        // 先封禁玩家
        testPlayer.setIsBanned(true);
        testPlayer.setBanReason("测试封禁");
        playerRepository.save(testPlayer);
        
        // 解除封禁
        int updated = playerRepository.unbanPlayer(testPlayer.getPlayerId());
        
        assertEquals(1, updated, "应该更新1个玩家");
        
        Optional<Player> updatedPlayer = playerRepository.findById(testPlayer.getPlayerId());
        assertTrue(updatedPlayer.isPresent(), "应该找到玩家");
        assertFalse(updatedPlayer.get().getIsBanned(), "玩家应该未被封禁");
        assertNull(updatedPlayer.get().getBanReason(), "封禁原因应该为空");
        assertNull(updatedPlayer.get().getBanEndTime(), "封禁结束时间应该为空");
    }
    
    @Test
    @DisplayName("更新玩家登录信息")
    void testUpdateLoginInfo() {
        LocalDateTime loginTime = LocalDateTime.now();
        loginTime = loginTime.withNano((loginTime.getNano() / 1000) * 1000);
        int updated = playerRepository.updateLoginInfo(testPlayer.getPlayerId(), loginTime);
        
        assertEquals(1, updated, "应该更新1个玩家");
        
        Optional<Player> updatedPlayer = playerRepository.findById(testPlayer.getPlayerId());
        assertTrue(updatedPlayer.isPresent(), "应该找到玩家");
        assertEquals(loginTime, updatedPlayer.get().getLastLoginTime(), "最后登录时间应该更新");
        assertEquals(2, updatedPlayer.get().getLoginCount(), "登录次数应该增加1");
    }
}